package com.mkx.multitier.monitor;

import com.mkx.multitier.Monitor;
import com.mkx.multitier.ResourceUsage;
import com.mkx.multitier.Task;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 标准监控组件，实现Monitor接口，用于监控多级分层线程模型的运行状态
 */
public class StandardMonitor implements Monitor {
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
    private final AtomicLong totalTasksProcessed = new AtomicLong(0);
    private final AtomicLong totalTasksFailed = new AtomicLong(0);
    private final Map<String, LayerStatistics> layerStatistics = new ConcurrentHashMap<>();
    private final boolean verbose;
    
    public StandardMonitor() {
        this(false); // 默认不输出详细日志
    }
    
    public StandardMonitor(boolean verbose) {
        this.verbose = verbose;
    }
    
    @Override
    public void onLayerInitialized(String layerName) {
        layerStatistics.putIfAbsent(layerName, new LayerStatistics(layerName));
        log("[" + layerName + "] Initialized");
    }
    
    @Override
    public void onLayerStarted(String layerName) {
        LayerStatistics stats = layerStatistics.get(layerName);
        if (stats != null) {
            stats.started = true;
            stats.startTime = System.currentTimeMillis();
        }
        log("[" + layerName + "] Started");
    }
    
    @Override
    public void onLayerShutdown(String layerName) {
        LayerStatistics stats = layerStatistics.get(layerName);
        if (stats != null) {
            stats.started = false;
            stats.shutdownTime = System.currentTimeMillis();
        }
        log("[" + layerName + "] Shutdown");
    }
    
    @Override
    public void onTaskProcessed(String layerName, Task task, long processTimeMs) {
        LayerStatistics stats = layerStatistics.get(layerName);
        if (stats != null) {
            stats.tasksProcessed.incrementAndGet();
            stats.totalProcessTime.addAndGet(processTimeMs);
            stats.updateAvgProcessTime();
        }
        
        totalTasksProcessed.incrementAndGet();
        
        if (verbose) {
            log("[" + layerName + "] Task processed: " + task.getTaskId() + 
                " (Priority: " + task.getPriority() + ", Time: " + processTimeMs + "ms)");
        }
    }
    
    @Override
    public void onTaskFailed(String layerName, Task task, Exception e, long processTimeMs) {
        LayerStatistics stats = layerStatistics.get(layerName);
        if (stats != null) {
            stats.tasksFailed.incrementAndGet();
        }
        
        totalTasksFailed.incrementAndGet();
        
        log("[" + layerName + "] Task failed: " + task.getTaskId() + 
            " (Time: " + processTimeMs + "ms, Error: " + e.getMessage() + ")");
        if (verbose) {
            e.printStackTrace();
        }
    }
    
    @Override
    public void reportResourceUsage(String layerName, ResourceUsage resourceUsage) {
        LayerStatistics stats = layerStatistics.computeIfAbsent(layerName, k -> new LayerStatistics(layerName));
        stats.lastResourceUsage = resourceUsage;
        
        if (verbose) {
            log("[Monitor] Resource usage for " + layerName + ": ");
            log("  Active tasks: " + resourceUsage.getActiveTasks());
            log("  Thread count: " + resourceUsage.getThreadCount());
            log("  Queue size: " + resourceUsage.getQueueSize());
            log("  Completed tasks: " + resourceUsage.getCompletedTasks());
            log("  Failed tasks: " + resourceUsage.getFailedTasks());
        }
    }
    
    @Override
    public void onResourceAllocated(String layerName, String resourceType, Object resource) {
        LayerStatistics stats = layerStatistics.get(layerName);
        if (stats != null) {
            stats.resourceAllocationCount.incrementAndGet();
        }
        
        if (verbose) {
            log("[" + layerName + "] Resource allocated: " + resourceType + ", total: " + 
                (stats != null ? stats.resourceAllocationCount.get() : 0));
        }
    }
    
    @Override
    public void onResourceReleased(String layerName, String resourceType, Object resource) {
        LayerStatistics stats = layerStatistics.get(layerName);
        if (stats != null) {
            stats.resourceReleaseCount.incrementAndGet();
        }
        
        if (verbose) {
            log("[" + layerName + "] Resource released: " + resourceType + ", total: " + 
                (stats != null ? stats.resourceReleaseCount.get() : 0));
        }
    }
    
    /**
     * 打印当前监控统计信息
     */
    public void printStatistics() {
        System.out.println("\n=== Multitier Thread Model Statistics ===");
        System.out.println("Total Tasks Processed: " + totalTasksProcessed.get());
        System.out.println("Total Tasks Failed: " + totalTasksFailed.get());
        System.out.println("Success Rate: " + String.format("%.2f%%", 
                totalTasksProcessed.get() > 0 ? 
                (double)(totalTasksProcessed.get() - totalTasksFailed.get()) / totalTasksProcessed.get() * 100 : 0));
        
        System.out.println("\nLayer Statistics:");
        for (LayerStatistics stats : layerStatistics.values()) {
            stats.print();
        }
        System.out.println("======================================\n");
    }
    
    /**
     * 获取特定层次的统计信息
     */
    public LayerStatistics getLayerStatistics(String layerName) {
        return layerStatistics.get(layerName);
    }
    
    /**
     * 日志记录辅助方法
     */
    private void log(String message) {
        String timestamp = DATE_FORMAT.format(new Date());
        System.out.println("[" + timestamp + "] " + message);
    }
    
    /**
     * 层次统计信息类
     */
    public static class LayerStatistics {
        private final String layerName;
        private final AtomicLong tasksProcessed = new AtomicLong(0);
        private final AtomicLong tasksFailed = new AtomicLong(0);
        private final AtomicLong totalProcessTime = new AtomicLong(0);
        private volatile double avgProcessTime = 0;
        private volatile ResourceUsage lastResourceUsage;
        private volatile boolean started = false;
        private volatile long startTime = 0;
        private volatile long shutdownTime = 0;
        private final AtomicLong resourceAllocationCount = new AtomicLong(0);
        private final AtomicLong resourceReleaseCount = new AtomicLong(0);
        
        public LayerStatistics(String layerName) {
            this.layerName = layerName;
        }
        
        private void updateAvgProcessTime() {
            long processed = tasksProcessed.get();
            if (processed > 0) {
                avgProcessTime = totalProcessTime.get() / (double) processed;
            }
        }
        
        public void print() {
            System.out.println("  - " + layerName + ":");
            System.out.println("    Status: " + (started ? "RUNNING" : "STOPPED"));
            System.out.println("    Tasks Processed: " + tasksProcessed.get());
            System.out.println("    Tasks Failed: " + tasksFailed.get());
            System.out.println("    Avg Process Time: " + String.format("%.2fms", avgProcessTime));
            
            if (lastResourceUsage != null) {
                System.out.println("    Resource Usage: " + lastResourceUsage);
            }
            
            if (startTime > 0) {
                long uptime = shutdownTime > 0 ? shutdownTime - startTime : System.currentTimeMillis() - startTime;
                System.out.println("    Uptime: " + formatDuration(uptime));
            }
        }
        
        private String formatDuration(long ms) {
            long seconds = ms / 1000;
            long minutes = seconds / 60;
            long hours = minutes / 60;
            
            if (hours > 0) {
                return hours + "h " + (minutes % 60) + "m " + (seconds % 60) + "s";
            } else if (minutes > 0) {
                return minutes + "m " + (seconds % 60) + "s";
            } else {
                return seconds + "s";
            }
        }
        
        // Getters
        public String getLayerName() { return layerName; }
        public long getTasksProcessed() { return tasksProcessed.get(); }
        public long getTasksFailed() { return tasksFailed.get(); }
        public double getAvgProcessTime() { return avgProcessTime; }
        public ResourceUsage getLastResourceUsage() { return lastResourceUsage; }
        public boolean isStarted() { return started; }
        public long getResourceAllocationCount() { return resourceAllocationCount.get(); }
        public long getResourceReleaseCount() { return resourceReleaseCount.get(); }
    }
}
